home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / imagemap / imap_polygon.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-07-20  |  21.9 KB  |  790 lines

  1. /*
  2.  * This is a plug-in for the GIMP.
  3.  *
  4.  * Generates clickable image maps.
  5.  *
  6.  * Copyright (C) 1998-1999 Maurits Rijk  lpeek.mrijk@consunet.nl
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  *
  22.  */
  23.  
  24. #include "config.h"
  25.  
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28.  
  29. #include "imap_cmd_delete_point.h"
  30. #include "imap_cmd_insert_point.h"
  31. #include "imap_main.h"
  32. #include "imap_misc.h"
  33. #include "imap_object_popup.h"
  34. #include "imap_polygon.h"
  35. #include "imap_table.h"
  36.  
  37. #include "libgimp/stdplugins-intl.h"
  38.  
  39. #include "polygon.xpm"
  40.  
  41. #define MAX_POLYGON_POINTS 99
  42.  
  43. static gboolean polygon_is_valid(Object_t *obj);
  44. static void polygon_destruct(Object_t *obj);
  45. static Object_t *polygon_clone(Object_t *obj);
  46. static void polygon_assign(Object_t *obj, Object_t *des);
  47. static void polygon_draw(Object_t* obj, GdkWindow *window, GdkGC* gc);
  48. static void polygon_draw_sashes(Object_t* obj, GdkWindow *window, GdkGC* gc);
  49. static MoveSashFunc_t polygon_near_sash(Object_t *obj, gint x, gint y);
  50. static gboolean polygon_point_is_on(Object_t *obj, gint x, gint y);
  51. static void polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
  52.                    gint *width, gint *height);
  53. static void polygon_resize(Object_t *obj, gint percentage_x,
  54.                gint percentage_y);
  55. static void polygon_move(Object_t *obj, gint dx, gint dy);
  56. static gpointer polygon_create_info_widget(GtkWidget *frame);
  57. static void polygon_update_info_widget(Object_t *obj, gpointer data);
  58. static void polygon_fill_info_tab(Object_t *obj, gpointer data);
  59. static void polygon_set_initial_focus(Object_t *obj, gpointer data);
  60. static void polygon_update(Object_t* obj, gpointer data);
  61. static void polygon_write_csim(Object_t* obj, gpointer param, 
  62.                    OutputFunc_t output);
  63. static void polygon_write_cern(Object_t* obj, gpointer param, 
  64.                    OutputFunc_t output);
  65. static void polygon_write_ncsa(Object_t* obj, gpointer param, 
  66.                    OutputFunc_t output);
  67. static void polygon_do_popup(Object_t *obj, GdkEventButton *event);
  68. static char** polygon_get_icon_data(void);
  69.  
  70. static ObjectClass_t polygon_class = {
  71.    N_("Polygon"),
  72.    NULL,            /* info_dialog */
  73.    NULL,            /* icon */
  74.    NULL,            /* mask */
  75.  
  76.    polygon_is_valid,
  77.    polygon_destruct,
  78.    polygon_clone,
  79.    polygon_assign,
  80.    NULL,            /* polygon_normalize */
  81.    polygon_draw,
  82.    polygon_draw_sashes,
  83.    polygon_near_sash,
  84.    polygon_point_is_on,
  85.    polygon_get_dimensions,
  86.    polygon_resize,
  87.    polygon_move,
  88.    polygon_create_info_widget,
  89.    polygon_update_info_widget,
  90.    polygon_fill_info_tab,
  91.    polygon_set_initial_focus,
  92.    polygon_update,
  93.    polygon_write_csim,
  94.    polygon_write_cern,
  95.    polygon_write_ncsa,
  96.    polygon_do_popup,
  97.    polygon_get_icon_data
  98. };
  99.  
  100. Object_t*
  101. create_polygon(GList *points)
  102. {
  103.    Polygon_t *polygon = g_new(Polygon_t, 1);
  104.    polygon->points = points;
  105.    return object_init(&polygon->obj, &polygon_class);
  106. }
  107.  
  108. static void
  109. polygon_free_list(Polygon_t *polygon)
  110. {
  111.    g_list_foreach(polygon->points, (GFunc) g_free, NULL);
  112.    g_list_free(polygon->points);
  113.    polygon->points = NULL;
  114. }
  115.  
  116. static void
  117. polygon_destruct(Object_t *obj)
  118. {
  119.    Polygon_t *polygon = ObjectToPolygon(obj);
  120.    polygon_free_list(polygon);
  121. }
  122.  
  123. static gboolean 
  124. polygon_is_valid(Object_t *obj)
  125. {
  126.    return g_list_length(ObjectToPolygon(obj)->points) > 2;
  127. }
  128.  
  129. static Object_t*
  130. polygon_clone(Object_t *obj)
  131. {
  132.    Polygon_t *polygon = ObjectToPolygon(obj);
  133.    Polygon_t *clone = g_new(Polygon_t, 1);
  134.    GList     *p;
  135.    
  136.    clone->points = NULL;
  137.    for (p = polygon->points; p; p = p->next) {
  138.       GdkPoint *point = (GdkPoint*) p->data;
  139.       clone->points = g_list_append(clone->points, 
  140.                     new_point(point->x, point->y));
  141.    }
  142.    return &clone->obj;
  143. }
  144.  
  145. static void
  146. polygon_assign(Object_t *obj, Object_t *des)
  147. {
  148.    Polygon_t *src_polygon = ObjectToPolygon(obj);
  149.    Polygon_t *des_polygon = ObjectToPolygon(des);
  150.    GList     *p;
  151.  
  152.    polygon_free_list(des_polygon);
  153.    for (p = src_polygon->points; p; p = p->next) {
  154.       GdkPoint *point = (GdkPoint*) p->data;
  155.       des_polygon->points = g_list_append(des_polygon->points, 
  156.                       new_point(point->x, point->y));
  157.    }
  158. }
  159.  
  160. static void
  161. polygon_draw(Object_t *obj, GdkWindow *window, GdkGC *gc)
  162. {
  163.    Polygon_t *polygon = ObjectToPolygon(obj);
  164.    draw_polygon(window, gc, polygon->points);
  165. }
  166.  
  167. static void
  168. polygon_draw_sashes(Object_t *obj, GdkWindow *window, GdkGC *gc)
  169. {
  170.    Polygon_t *polygon = ObjectToPolygon(obj);
  171.    GList     *p;
  172.    for (p = polygon->points; p; p = p->next) {
  173.       GdkPoint *point = (GdkPoint*) p->data;
  174.       draw_sash(window, gc, point->x, point->y);
  175.    }
  176. }
  177.  
  178. static GdkPoint *_sash_point;
  179. static gint _sash_index;
  180.  
  181. static void
  182. move_sash(Object_t *obj, gint dx, gint dy)
  183. {
  184.    _sash_point->x += dx;
  185.    _sash_point->y += dy;
  186. }
  187.  
  188. static MoveSashFunc_t
  189. polygon_near_sash(Object_t *obj, gint x, gint y)
  190. {
  191.    Polygon_t *polygon = ObjectToPolygon(obj);
  192.    GList     *p;
  193.  
  194.    _sash_index = 0;
  195.    for (p = polygon->points; p; p = p->next, _sash_index++) {
  196.       GdkPoint *point = (GdkPoint*) p->data;
  197.       if (near_sash(point->x, point->y, x, y)) {
  198.      _sash_point = point;
  199.      return move_sash;
  200.       }
  201.    }
  202.    return NULL;
  203. }
  204.  
  205. static gboolean
  206. right_intersect(GdkPoint *p1, GdkPoint *p2, gint x, gint y)
  207. {
  208.    gint dx = p2->x - p1->x;
  209.    gint dy = p2->y - p1->y;
  210.  
  211.    if ((dy > 0 && y > p1->y && y < p2->y) ||
  212.        (dy < y && y > p2->y && y < p1->y)) {
  213.       gint sx = p1->x + (y - p1->y) * dx / dy;
  214.       return sx > x;
  215.    }
  216.    return FALSE;
  217. }
  218.  
  219. static gboolean
  220. polygon_point_is_on(Object_t *obj, gint x, gint y)
  221. {
  222.    Polygon_t *polygon = ObjectToPolygon(obj);
  223.    GList     *p;
  224.    int        count = 0;
  225.    GdkPoint  *first, *prev;
  226.  
  227.    p = polygon->points;
  228.    first = prev = (GdkPoint*) p->data;
  229.    p = p->next;
  230.  
  231.    for (; p; p = p->next) {
  232.       GdkPoint *point = (GdkPoint*) p->data;
  233.       if (right_intersect(prev, point, x, y))
  234.      count++;
  235.       prev = point;
  236.    }
  237.    if (right_intersect(prev, first, x, y))
  238.        count++;
  239.  
  240.    return count % 2;
  241. }
  242.  
  243. static void 
  244. polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
  245.                gint *width, gint *height)
  246. {
  247.    Polygon_t *polygon = ObjectToPolygon(obj);
  248.    gint min_x = G_MAXINT, min_y = G_MAXINT;
  249.    gint max_x = G_MININT, max_y = G_MININT;
  250.    GList     *p;
  251.  
  252.    for (p = polygon->points; p; p = p->next) {
  253.       GdkPoint *point = (GdkPoint*) p->data;
  254.       if (point->x < min_x)
  255.      min_x = point->x;
  256.       if (point->x > max_x)
  257.      max_x = point->x;
  258.       if (point->y < min_y)
  259.      min_y = point->y;
  260.       if (point->y > max_y)
  261.      max_y = point->y;
  262.    }
  263.    *x = min_x;
  264.    *y = min_y;
  265.    *width = max_x - min_x;
  266.    *height = max_y - min_y;
  267. }
  268.  
  269. static void 
  270. polygon_resize(Object_t *obj, gint percentage_x, gint percentage_y)
  271. {
  272.    Polygon_t *polygon = ObjectToPolygon(obj);
  273.    GList     *p;
  274.    for (p = polygon->points; p; p = p->next) {
  275.       GdkPoint *point = (GdkPoint*) p->data;
  276.       point->x = point->x * percentage_x / 100;
  277.       point->y = point->y * percentage_y / 100;
  278.    }
  279. }
  280.  
  281. static void
  282. polygon_move(Object_t *obj, gint dx, gint dy)
  283. {
  284.    Polygon_t *polygon = ObjectToPolygon(obj);
  285.    GList     *p;
  286.    for (p = polygon->points; p; p = p->next) {
  287.       GdkPoint *point = (GdkPoint*) p->data;
  288.       point->x += dx;
  289.       point->y += dy;
  290.    }
  291. }
  292.  
  293. typedef struct {
  294.    Object_t  *obj;
  295.    GtkWidget *list;
  296.    GtkWidget *x;
  297.    GtkWidget *y;
  298.    GtkWidget *update;
  299.    GtkWidget *insert;
  300.    GtkWidget *append;
  301.    GtkWidget *remove;
  302.    gint          selected_row;
  303.    gint          timeout;
  304. } PolygonProperties_t;
  305.  
  306. static void
  307. select_row_cb(GtkWidget *widget, gint row, gint column, GdkEventButton *event,
  308.           PolygonProperties_t *data)
  309. {
  310.    gchar *text;
  311.    data->selected_row = row;
  312.  
  313.    _sash_point = g_list_nth(ObjectToPolygon(data->obj)->points, row)->data;
  314.  
  315.    gtk_clist_get_text(GTK_CLIST(data->list), row, 0, &text);
  316.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->x), atoi(text));
  317.  
  318.    gtk_clist_get_text(GTK_CLIST(data->list), row, 1, &text);
  319.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->y), atoi(text));
  320. }
  321.  
  322. static void
  323. update_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  324. {
  325.    gtk_clist_set_text(GTK_CLIST(data->list), data->selected_row, 0,
  326.               gtk_entry_get_text(GTK_ENTRY(data->x)));
  327.    gtk_clist_set_text(GTK_CLIST(data->list), data->selected_row, 1,
  328.               gtk_entry_get_text(GTK_ENTRY(data->y)));
  329. }
  330.  
  331. static void
  332. set_buttons_sensitivity(PolygonProperties_t *data)
  333. {
  334.    gint rows = GTK_CLIST(data->list)->rows;
  335.    gint sensitive = (rows != MAX_POLYGON_POINTS) && (rows > 2);
  336.    gtk_widget_set_sensitive(data->insert, sensitive);
  337.    gtk_widget_set_sensitive(data->append, sensitive);
  338. }
  339.  
  340. static void
  341. insert_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  342. {
  343.    gchar *text[2];
  344.    text[0] = gtk_entry_get_text(GTK_ENTRY(data->x));
  345.    text[1] = gtk_entry_get_text(GTK_ENTRY(data->y));
  346.    gtk_clist_insert(GTK_CLIST(data->list), data->selected_row, text);
  347.    set_buttons_sensitivity(data);
  348. }
  349.  
  350. static void
  351. append_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  352. {
  353.    gchar *text[2];
  354.    text[0] = gtk_entry_get_text(GTK_ENTRY(data->x));
  355.    text[1] = gtk_entry_get_text(GTK_ENTRY(data->y));
  356.    gtk_clist_append(GTK_CLIST(data->list), text);
  357.    set_buttons_sensitivity(data);
  358. }
  359.  
  360. static void
  361. remove_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  362. {
  363.    gtk_clist_remove(GTK_CLIST(data->list), data->selected_row);
  364.    set_buttons_sensitivity(data);
  365. }
  366.  
  367. static void
  368. x_changed_cb(GtkWidget *widget, gpointer data)
  369. {
  370.    Object_t *obj = ((PolygonProperties_t*) data)->obj;
  371.    gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
  372.    _sash_point->x = x;
  373.    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
  374. }
  375.  
  376. static void
  377. y_changed_cb(GtkWidget *widget, gpointer data)
  378. {
  379.    Object_t *obj = ((PolygonProperties_t*) data)->obj;
  380.    gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
  381.    _sash_point->y = y;
  382.    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
  383. }
  384.  
  385. static gpointer
  386. polygon_create_info_widget(GtkWidget *frame)
  387. {
  388.    PolygonProperties_t *props = g_new(PolygonProperties_t, 1);
  389.    GtkWidget *hbox, *swin, *table;
  390.    GtkWidget *list;
  391.    gint max_width = get_image_width();
  392.    gint max_height = get_image_height();
  393.    gchar *titles[] = {N_("x (pixels)"), N_("y (pixels)")};
  394.    gint i;
  395.  
  396.    hbox = gtk_hbox_new(FALSE, 1);
  397.    gtk_container_add(GTK_CONTAINER(frame), hbox);
  398.    gtk_widget_show(hbox);
  399.  
  400.    for (i = 0; i < 2; i++)
  401.      titles[i] = gettext(titles[i]);
  402.    props->list = list = gtk_clist_new_with_titles(2, titles);
  403.    gtk_clist_column_titles_passive(GTK_CLIST(list));
  404.  
  405.    swin = gtk_scrolled_window_new(NULL, NULL);
  406.    gtk_container_set_border_width(GTK_CONTAINER(swin), 10);
  407.  
  408.    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), 
  409.                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  410.  
  411.    gtk_box_pack_start(GTK_BOX(hbox), swin, FALSE, FALSE, FALSE);
  412.    gtk_widget_show(swin);
  413.  
  414.    gtk_clist_set_shadow_type(GTK_CLIST(list), GTK_SHADOW_ETCHED_IN);
  415.    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin), list);
  416.  
  417.    gtk_clist_set_column_width(GTK_CLIST(list), 0, 48);
  418.    gtk_clist_set_column_width(GTK_CLIST(list), 1, 48);
  419.    gtk_clist_set_column_justification(GTK_CLIST(list), 0, GTK_JUSTIFY_RIGHT);
  420.    gtk_clist_set_column_justification(GTK_CLIST(list), 1, GTK_JUSTIFY_RIGHT);
  421.  
  422.    gtk_signal_connect(GTK_OBJECT(list), "select_row",
  423.               GTK_SIGNAL_FUNC(select_row_cb), props);
  424.    gtk_clist_set_selection_mode(GTK_CLIST(list), GTK_SELECTION_SINGLE);
  425.    gtk_widget_show(list);
  426.  
  427.    table = gtk_table_new(6, 3, FALSE);
  428.    gtk_container_set_border_width(GTK_CONTAINER(table), 10);
  429.    gtk_table_set_row_spacings(GTK_TABLE(table), 10);
  430.    gtk_table_set_col_spacings(GTK_TABLE(table), 10);
  431.    gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, FALSE);
  432.    gtk_widget_show(table);
  433.  
  434.    create_label_in_table(table, 0, 0, "x:");
  435.    props->x = create_spin_button_in_table(table, 0, 1, 1, 0, max_width - 1);
  436.    gtk_signal_connect(GTK_OBJECT(props->x), "changed", 
  437.               (GtkSignalFunc) x_changed_cb, (gpointer) props);
  438.    gtk_widget_set_usize(props->x, 64, -1);
  439.    create_label_in_table(table, 0, 2, _("pixels"));
  440.  
  441.    create_label_in_table(table, 1, 0, "y:");
  442.    props->y = create_spin_button_in_table(table, 1, 1, 1, 0, max_height - 1);
  443.    gtk_signal_connect(GTK_OBJECT(props->y), "changed", 
  444.               (GtkSignalFunc) y_changed_cb, (gpointer) props);
  445.    gtk_widget_set_usize(props->y, 64, -1);
  446.    create_label_in_table(table, 1, 2, _("pixels"));
  447.  
  448.    props->update = gtk_button_new_with_label(_("Update"));
  449.    gtk_signal_connect(GTK_OBJECT(props->update), "clicked",
  450.               GTK_SIGNAL_FUNC(update_button_clicked), props);
  451.    gtk_table_attach_defaults(GTK_TABLE(table), props->update, 1, 2, 2, 3);
  452.    gtk_widget_show(props->update);
  453.  
  454.    props->insert = gtk_button_new_with_label(_("Insert"));
  455.    gtk_signal_connect(GTK_OBJECT(props->insert), "clicked",
  456.               GTK_SIGNAL_FUNC(insert_button_clicked), props);
  457.    gtk_table_attach_defaults(GTK_TABLE(table), props->insert, 1, 2, 3, 4);
  458.    gtk_widget_show(props->insert);
  459.  
  460.    props->append = gtk_button_new_with_label(_("Append"));
  461.    gtk_signal_connect(GTK_OBJECT(props->append), "clicked",
  462.               GTK_SIGNAL_FUNC(append_button_clicked), props);
  463.    gtk_table_attach_defaults(GTK_TABLE(table), props->append, 1, 2, 4, 5);
  464.    gtk_widget_show(props->append);
  465.  
  466.    props->remove = gtk_button_new_with_label(_("Remove"));
  467.    gtk_signal_connect(GTK_OBJECT(props->remove), "clicked",
  468.               GTK_SIGNAL_FUNC(remove_button_clicked), props);
  469.    gtk_table_attach_defaults(GTK_TABLE(table), props->remove, 1, 2, 5, 6);
  470.    gtk_widget_show(props->remove);
  471.  
  472.    props->timeout = 0;
  473.  
  474.    return props;
  475. }
  476.  
  477. static gint
  478. update_timeout(gpointer data)
  479. {
  480.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  481.    polygon_fill_info_tab(props->obj, data);
  482.    return FALSE;
  483. }
  484.  
  485. static void
  486. polygon_update_info_widget(Object_t *obj, gpointer data)
  487. {
  488.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  489.    
  490.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), _sash_point->x);
  491.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), _sash_point->y);
  492.    
  493.    if (props->selected_row != _sash_index) {
  494.       props->selected_row = _sash_index;
  495.       gtk_clist_select_row(GTK_CLIST(props->list), _sash_index, -1);
  496.    }
  497.    if (props->timeout)
  498.       gtk_timeout_remove(props->timeout);
  499.    props->timeout = gtk_timeout_add(1000, update_timeout, data);
  500. }
  501.  
  502. static void
  503. polygon_fill_info_tab(Object_t *obj, gpointer data)
  504. {
  505.    Polygon_t *polygon = ObjectToPolygon(obj);
  506.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  507.    GList     *p;
  508.  
  509.    props->obj = obj;
  510.    gtk_clist_freeze(GTK_CLIST(props->list));
  511.    gtk_clist_clear(GTK_CLIST(props->list));
  512.    for (p = polygon->points; p; p = p->next) {
  513.       GdkPoint *point = (GdkPoint*) p->data;
  514.       char x[16], y[16];
  515.       char *text[2];
  516.  
  517.       text[0] = x;
  518.       text[1] = y;
  519.       sprintf(x, "%d", point->x);
  520.       sprintf(y, "%d", point->y);
  521.       gtk_clist_append(GTK_CLIST(props->list), text);
  522.    }
  523.    gtk_clist_select_row(GTK_CLIST(props->list), _sash_index, -1);
  524.    gtk_clist_thaw(GTK_CLIST(props->list));
  525.  
  526.    set_buttons_sensitivity(props);
  527. }
  528.  
  529. static void 
  530. polygon_set_initial_focus(Object_t *obj, gpointer data)
  531. {
  532.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  533.    gtk_widget_grab_focus(props->x);
  534. }
  535.  
  536. static void 
  537. polygon_update(Object_t* obj, gpointer data)
  538. {
  539.    Polygon_t *polygon = ObjectToPolygon(obj);
  540.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  541.    gint rows = GTK_CLIST(props->list)->rows;
  542.    int i;
  543.  
  544.    g_list_free(polygon->points);
  545.    polygon->points = NULL;
  546.  
  547.    for (i = 0; i < rows; i++) {
  548.       gchar *text;
  549.       GdkPoint *point = g_new(GdkPoint, 1);
  550.  
  551.       gtk_clist_get_text(GTK_CLIST(props->list), i, 0, &text);
  552.       point->x = atoi(text);
  553.       gtk_clist_get_text(GTK_CLIST(props->list), i, 1, &text);
  554.       point->y = atoi(text);
  555.  
  556.       polygon->points = g_list_append(polygon->points, point);
  557.    }
  558. }
  559.  
  560. static void
  561. polygon_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
  562. {
  563.    Polygon_t *polygon = ObjectToPolygon(obj);
  564.    GList     *p;
  565.  
  566.    output(param, "\"poly\" coords=\"");
  567.    for (p = polygon->points; p; p = p->next) {
  568.       GdkPoint *point = (GdkPoint*) p->data;
  569.       output(param, "%d,%d", point->x, point->y);
  570.       output(param, "%c", (p->next) ? ',' : '"');
  571.    }
  572. }
  573.  
  574. static void
  575. polygon_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
  576. {
  577.    Polygon_t *polygon = ObjectToPolygon(obj);
  578.    GList     *p;
  579.    GdkPoint  *first = (GdkPoint*) polygon->points->data;
  580.  
  581.    output(param, "poly ");
  582.    for (p = polygon->points; p; p = p->next) {
  583.       GdkPoint *point = (GdkPoint*) p->data;
  584.       output(param, "(%d,%d) ", point->x, point->y);
  585.    }
  586.    output(param, "(%d,%d)", first->x, first->y);
  587. }
  588.  
  589. static void
  590. polygon_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
  591. {
  592.    Polygon_t *polygon = ObjectToPolygon(obj);
  593.    GList     *p;
  594.    GdkPoint  *first = (GdkPoint*) polygon->points->data;
  595.  
  596.    output(param, "poly %s", obj->url);
  597.    for (p = polygon->points; p; p = p->next) {
  598.       GdkPoint *point = (GdkPoint*) p->data;
  599.       output(param, " %d,%d", point->x, point->y);
  600.    }
  601.    output(param, " %d,%d", first->x, first->y);
  602. }
  603.  
  604. static gint _insert_edge;
  605. static gint _insert_x;
  606. static gint _insert_y;
  607.  
  608. static void
  609. polygon_insert_point(GtkWidget *widget, gpointer data)
  610. {
  611.    Command_t *command = insert_point_command_new(get_popup_object(), _insert_x,
  612.                          _insert_y, _insert_edge);
  613.    command_execute(command);
  614. }
  615.  
  616. static void
  617. polygon_delete_point(GtkWidget *widget, gpointer data)
  618. {
  619.    Command_t *command = delete_point_command_new(get_popup_object(), 
  620.                          _sash_point);
  621.    command_execute(command);
  622. }
  623.  
  624. static gboolean
  625. point_near_edge(GdkPoint *first, GdkPoint *second, gint x, gint y)
  626. {
  627.    gint den, nom;
  628.    gdouble u;
  629.  
  630.    den = (first->x - x) * (first->x - second->x) +
  631.       (first->y - y) * (first->y - second->y);
  632.    nom = (second->x - first->x) * (second->x - first->x) +
  633.       (second->y - first->y) * (second->y - first->y);
  634.    u = (gdouble) den / nom;
  635.    if (u >= 0.0 && u <= 1.0) {
  636.       gint sx = first->x + (gint) (u * (second->x - first->x)) - x;
  637.       gint sy = first->y + (gint) (u * (second->y - first->y)) - y;
  638.       return sx * sx + sy * sy <= 25; /* Fix me! */
  639.    }
  640.    return 0;
  641. }
  642.  
  643. static gint
  644. polygon_near_edge(Object_t *obj, gint x, gint y)
  645. {
  646.    Polygon_t *polygon = ObjectToPolygon(obj);
  647.    GList     *p = polygon->points;
  648.    GdkPoint  *first = (GdkPoint*) p->data;
  649.    GdkPoint  *prev = first;
  650.    gint n = 1;
  651.  
  652.    for (p = p->next; p; p = p->next, n++) {
  653.       GdkPoint *next = (GdkPoint*) p->data;
  654.       if (point_near_edge(prev, next, x, y))
  655.      return n;
  656.       prev = next;
  657.    }
  658.    return (point_near_edge(prev, first, x, y)) ? n + 1 : 0;
  659. }
  660.  
  661. static void 
  662. polygon_do_popup(Object_t *obj, GdkEventButton *event)
  663. {
  664.    gint x = get_real_coord((gint) event->x);
  665.    gint y = get_real_coord((gint) event->y);
  666.  
  667.    if (polygon_near_sash(obj, x, y)) {
  668.       static ObjectPopup_t *delete_popup;
  669.       if (!delete_popup) {
  670.      delete_popup = make_object_popup();
  671.      object_popup_prepend_menu(delete_popup, _("Delete Point"), 
  672.                    polygon_delete_point, delete_popup);
  673.       }
  674.       object_handle_popup(delete_popup, obj, event);
  675.    } else {
  676.       _insert_edge = polygon_near_edge(obj, x, y);
  677.       if (_insert_edge) {
  678.      static ObjectPopup_t *insert_popup;
  679.  
  680.      _insert_x = x;
  681.      _insert_y = y;
  682.  
  683.      if (!insert_popup) {
  684.         insert_popup = make_object_popup();
  685.         object_popup_prepend_menu(insert_popup, _("Insert Point"), 
  686.                       polygon_insert_point, insert_popup);
  687.      }
  688.      object_handle_popup(insert_popup, obj, event);
  689.       } else {
  690.      object_do_popup(obj, event);
  691.       }
  692.    }
  693. }
  694.  
  695. static char** 
  696. polygon_get_icon_data(void)
  697. {
  698.    return polygon_xpm;
  699. }
  700.  
  701. static GList *_prev_link;
  702.  
  703. static gboolean
  704. polygon_factory_finish(Object_t *obj, gint x, gint y)
  705. {
  706.    Polygon_t *polygon = ObjectToPolygon(obj);
  707.    GdkPoint *prev_point = (GdkPoint*) _prev_link->data;
  708.  
  709.    if (x == prev_point->x && y == prev_point->y) {
  710.       polygon_remove_last_point(polygon);
  711.       return TRUE;
  712.    } else {
  713.       g_list_append(polygon->points, new_point(x, y));
  714.       _prev_link = _prev_link->next;
  715.    }
  716.    return FALSE;
  717. }
  718.  
  719. static gboolean
  720. polygon_factory_cancel(GdkEventButton *event, Object_t *obj)
  721. {
  722.    if (event->state & GDK_SHIFT_MASK) {
  723.       return TRUE;
  724.    } else {
  725.       Polygon_t *polygon = ObjectToPolygon(obj);
  726.       GList *link = _prev_link;
  727.         
  728.       _prev_link = _prev_link->prev;
  729.       g_free((GdkPoint*) link->data);
  730.       polygon->points = g_list_remove_link(polygon->points, link);
  731.    }
  732.    return _prev_link == NULL;
  733. }
  734.  
  735. static Object_t*
  736. polygon_factory_create_object(gint x, gint y)
  737. {
  738.    GList *points;
  739.  
  740.    points = _prev_link = g_list_append(NULL, new_point(x, y));
  741.    points = g_list_append(points, new_point(x, y));
  742.  
  743.    return create_polygon(points);
  744. }
  745.  
  746. static void
  747. polygon_factory_set_xy(Object_t *obj, guint state, gint x, gint y)
  748. {
  749.    Polygon_t *polygon = ObjectToPolygon(obj);
  750.    GList *last = g_list_last(polygon->points);
  751.    GdkPoint *point = (GdkPoint*) last->data;
  752.    GdkPoint *prev = (GdkPoint*) last->prev->data;
  753.  
  754.    point->x = x;
  755.    point->y = y;
  756.  
  757.    main_set_dimension(x - prev->x, y - prev->y);
  758. }
  759.  
  760. static ObjectFactory_t polygon_factory = {
  761.    NULL,            /* Object pointer */
  762.    polygon_factory_finish,
  763.    polygon_factory_cancel,
  764.    polygon_factory_create_object,
  765.    polygon_factory_set_xy
  766. };
  767.  
  768. ObjectFactory_t*
  769. get_polygon_factory(guint state)
  770. {
  771.    return &polygon_factory;
  772. }
  773.  
  774. void
  775. polygon_remove_last_point(Polygon_t *polygon)
  776. {
  777.    GList *last = g_list_last(polygon->points);
  778.    g_free((GdkPoint*) last->data);
  779.    polygon->points = g_list_remove_link(polygon->points, last);
  780. }
  781.  
  782. GdkPoint*
  783. new_point(gint x, gint y)
  784. {
  785.    GdkPoint *point = g_new(GdkPoint, 1);
  786.    point->x = x;
  787.    point->y = y;
  788.    return point;
  789. }
  790.